//
// OpenTemplate
// Copyright (c) 2006
// by OpenArrow Software ( http://www.openarrow.com )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions 
// of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using OpenArrow.Templating.Properties;
using OpenArrow.Reporting;
using System.Reflection;
using System.ComponentModel;

namespace OpenArrow.Templating.Execution
{
    /// <summary>
    /// Utility class that executes a compiled template
    /// </summary>
    public static class TemplateExecutor
    {
        #region ExecuteTemplate - From Type with Reporter to Stream

        /// <summary>
        /// Executes a compiled template type, rendering the output to a <see cref="System.IO.Stream"/>
        /// </summary>
        /// <param name="templateType">The compiled template type</param>
        /// <param name="output">The <see cref="System.IO.Stream"/> to render output to</param>
        /// <param name="reporter">An <see cref="IProgressReporter"/> to report progress to</param>
        /// <exception cref="ArgumentNullException"><paramref name="templateType"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(Type templateType, Stream output, IProgressReporter reporter)
        {
            if (templateType == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "templateType"), "templateType");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (reporter == null)
                reporter = new NullProgressReporter();

            ExecuteTemplate(templateType, output, new Dictionary<string, object>(), reporter);
        }

        /// <summary>
        /// Executes a compiled template type, rendering the output to a <see cref="System.IO.Stream"/>
        /// </summary>
        /// <param name="templateType">The compiled template type</param>
        /// <param name="output">The <see cref="System.IO.Stream"/> to render output to</param>
        /// <param name="properties">The parameters to pass to the template</param>
        /// <param name="reporter">An <see cref="IProgressReporter"/> to report progress to</param>
        /// <exception cref="ArgumentNullException"><paramref name="templateType"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="properties"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(Type templateType, Stream output, Dictionary<string, object> properties, IProgressReporter reporter)
        {
            if (templateType == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "templateType"), "templateType");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (properties == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "properties"), "properties");
            if (reporter == null)
                reporter = new NullProgressReporter();

            ExecuteTemplate(CreateTemplate(templateType), output, properties, reporter);
        }

        #endregion

        #region ExecuteTemplate - From Type with Reporter to TextWriter

        /// <summary>
        /// Executes a compiled template type, rendering the output to a <see cref="System.IO.TextWriter"/>
        /// </summary>
        /// <param name="templateType">The compiled template type</param>
        /// <param name="output">The <see cref="System.IO.TextWriter"/> to render output to</param>
        /// <param name="reporter">An <see cref="IProgressReporter"/> to report progress to</param>
        /// <exception cref="ArgumentNullException"><paramref name="templateType"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(Type templateType, TextWriter output, IProgressReporter reporter)
        {
            if (templateType == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "templateType"), "templateType");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (reporter == null)
                reporter = new NullProgressReporter();

            ExecuteTemplate(templateType, output, new Dictionary<string, object>(), reporter);
        }

        /// <summary>
        /// Executes a compiled template type, rendering the output to a <see cref="System.IO.TextWriter"/>
        /// </summary>
        /// <param name="templateType">The compiled template type</param>
        /// <param name="output">The <see cref="System.IO.TextWriter"/> to render output to</param>
        /// <param name="properties">The parameters to pass to the template</param>
        /// <param name="reporter">An <see cref="IProgressReporter"/> to report progress to</param>
        /// <exception cref="ArgumentNullException"><paramref name="templateType"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="properties"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(Type templateType, TextWriter output, Dictionary<string, object> properties, IProgressReporter reporter)
        {
            if (templateType == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "templateType"), "templateType");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (properties == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "properties"), "properties");
            if (reporter == null)
                reporter = new NullProgressReporter();

            ExecuteTemplate(CreateTemplate(templateType), output, properties, reporter);
        }

        #endregion

        #region ExecuteTemplate - From Object with Reporter to Stream

        /// <summary>
        /// Executes a compiled template object, rendering the output to a <see cref="System.IO.Stream"/>
        /// </summary>
        /// <param name="template">The compiled template object</param>
        /// <param name="output">The <see cref="System.IO.Stream"/> to render output to</param>
        /// <param name="reporter">An <see cref="IProgressReporter"/> to report progress to</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(ITemplate template, Stream output, IProgressReporter reporter)
        {
            if (template == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (reporter == null)
                reporter = new NullProgressReporter();

            ExecuteTemplate(template, output, new Dictionary<string, object>(), reporter);
        }

        /// <summary>
        /// Executes a compiled template object, rendering the output to a <see cref="System.IO.Stream"/>
        /// </summary>
        /// <param name="template">The compiled template object</param>
        /// <param name="output">The <see cref="System.IO.Stream"/> to render output to</param>
        /// <param name="properties">The parameters to pass to the template</param>
        /// <param name="reporter">An <see cref="IProgressReporter"/> to report progress to</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="properties"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(ITemplate template, Stream output, Dictionary<string, object> properties, IProgressReporter reporter)
        {
            if (template == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (properties == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "properties"), "properties");
            if (reporter == null)
                reporter = new NullProgressReporter();

            SetProperties(template, properties);
            template.Render(output, reporter);
        }

        #endregion

        #region ExecuteTemplate - From Object with Reporter to TextWriter

        /// <summary>
        /// Executes a compiled template object, rendering the output to a <see cref="System.IO.TextWriter"/>
        /// </summary>
        /// <param name="template">The compiled template object</param>
        /// <param name="output">The <see cref="System.IO.TextWriter"/> to render output to</param>
        /// <param name="reporter">An <see cref="IProgressReporter"/> to report progress to</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(ITemplate template, TextWriter output, IProgressReporter reporter)
        {
            if (template == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (reporter == null)
                reporter = new NullProgressReporter();

            ExecuteTemplate(template, output, new Dictionary<string, object>(), reporter);
        }

        /// <summary>
        /// Executes a compiled template object, rendering the output to a <see cref="System.IO.TextWriter"/>
        /// </summary>
        /// <param name="template">The compiled template object</param>
        /// <param name="output">The <see cref="System.IO.TextWriter"/> to render output to</param>
        /// <param name="properties">The parameters to pass to the template</param>
        /// <param name="reporter">An <see cref="IProgressReporter"/> to report progress to</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="properties"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(ITemplate template, TextWriter output, Dictionary<string, object> properties, IProgressReporter reporter)
        {
            if (template == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (properties == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "properties"), "properties");
            if (reporter == null)
                reporter = new NullProgressReporter();

            SetProperties(template, properties);
            template.Render(output, reporter);
        }

        #endregion

        #region ExecuteTemplate - All Parameters, No Reporter

        /// <summary>
        /// Executes a compiled template type, rendering the output to a <see cref="System.IO.Stream"/>
        /// </summary>
        /// <param name="templateType">The compiled template type</param>
        /// <param name="output">The <see cref="System.IO.Stream"/> to render output to</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(Type templateType, Stream output)
        {
            if (templateType == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            
            ExecuteTemplate(templateType, output, new NullProgressReporter());
        }

        /// <summary>
        /// Executes a compiled template type, rendering the output to a <see cref="System.IO.Stream"/>
        /// </summary>
        /// <param name="templateType">The compiled template type</param>
        /// <param name="output">The <see cref="System.IO.Stream"/> to render output to</param>
        /// <param name="properties">The parameters to pass to the template</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="properties"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(Type templateType, Stream output, Dictionary<string, object> properties)
        {
            if (templateType == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (properties == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "properties"), "properties");

            ExecuteTemplate(templateType, output, properties, new NullProgressReporter());
        }

        /// <summary>
        /// Executes a compiled template type, rendering the output to a <see cref="System.IO.TextWriter"/>
        /// </summary>
        /// <param name="templateType">The compiled template type</param>
        /// <param name="output">The <see cref="System.IO.TextWriter"/> to render output to</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(Type templateType, TextWriter output)
        {
            if (templateType == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");

            ExecuteTemplate(templateType, output, new NullProgressReporter());
        }

        /// <summary>
        /// Executes a compiled template type, rendering the output to a <see cref="System.IO.TextWriter"/>
        /// </summary>
        /// <param name="templateType">The compiled template type</param>
        /// <param name="output">The <see cref="System.IO.TextWriter"/> to render output to</param>
        /// <param name="properties">The parameters to pass to the template</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="properties"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(Type templateType, TextWriter output, Dictionary<string, object> properties)
        {
            if (templateType == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (properties == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "properties"), "properties");

            ExecuteTemplate(templateType, output, properties, new NullProgressReporter());
        }

        /// <summary>
        /// Executes a compiled template object, rendering the output to a <see cref="System.IO.Stream"/>
        /// </summary>
        /// <param name="template">The compiled template object</param>
        /// <param name="output">The <see cref="System.IO.Stream"/> to render output to</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(ITemplate template, Stream output)
        {
            if (template == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");

            ExecuteTemplate(template, output, new NullProgressReporter());
        }

        /// <summary>
        /// Executes a compiled template object, rendering the output to a <see cref="System.IO.Stream"/>
        /// </summary>
        /// <param name="template">The compiled template object</param>
        /// <param name="output">The <see cref="System.IO.Stream"/> to render output to</param>
        /// <param name="properties">The parameters to pass to the template</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="properties"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(ITemplate template, Stream output, Dictionary<string, object> properties)
        {
            if (template == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (properties == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "properties"), "properties");

            ExecuteTemplate(template, output, properties, new NullProgressReporter());
        }

        /// <summary>
        /// Executes a compiled template object, rendering the output to a <see cref="System.IO.TextWriter"/>
        /// </summary>
        /// <param name="template">The compiled template object</param>
        /// <param name="output">The <see cref="System.IO.TextWriter"/> to render output to</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(ITemplate template, TextWriter output)
        {
            if (template == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");

            ExecuteTemplate(template, output, new NullProgressReporter());
        }

        /// <summary>
        /// Executes a compiled template object, rendering the output to a <see cref="System.IO.TextWriter"/>
        /// </summary>
        /// <param name="template">The compiled template object</param>
        /// <param name="output">The <see cref="System.IO.TextWriter"/> to render output to</param>
        /// <param name="properties">The parameters to pass to the template</param>
        /// <exception cref="ArgumentNullException"><paramref name="template"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentNullException"><paramref name="properties"/> is <see langword="null"/></exception>
        public static void ExecuteTemplate(ITemplate template, TextWriter output, Dictionary<string, object> properties)
        {
            if (template == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "template"), "template");
            if (output == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "output"), "output");
            if (properties == null)
                throw new ArgumentNullException(String.Format(Resources.Exception_ArgIsNull, "properties"), "properties");

            ExecuteTemplate(template, output, properties, new NullProgressReporter());
        } 

        #endregion

        #region Private Helper Methods

        private static ITemplate CreateTemplate(Type templateType)
        {
            if (typeof(ITemplate).IsAssignableFrom(templateType))
                return (ITemplate)Activator.CreateInstance(templateType);
            throw new InvalidCastException(Resources.Error_TemplateDoesNotImplementITemplate);
        }

        private static void SetProperties(ITemplate template, Dictionary<string, object> properties)
        {
            Type templateType = template.GetType();
            foreach (string property in properties.Keys)
            {
                PropertyInfo propInfo = templateType.GetProperty(property);
                if (propInfo != null)
                {
                    object objValue = properties[propInfo.Name];
                    propInfo.SetValue(template, objValue, null);
                }
            }
        }

        #endregion
    }
}
